package zack.project.trigger.http;

import com.alibaba.fastjson.JSON;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.web.bind.annotation.*;
import zack.project.domain.activity.model.entity.*;
import zack.project.domain.activity.model.valobj.OrderTradeTypeVO;
import zack.project.domain.activity.service.IRaffleActivityAccountQuotaService;
import zack.project.domain.activity.service.IRaffleActivityPartakeService;
import zack.project.domain.activity.service.IRaffleActivitySkuProductService;
import zack.project.domain.activity.service.armory.IActivityArmory;
import zack.project.domain.award.model.entity.UserAwardRecordEntity;
import zack.project.domain.award.model.valobj.AwardStateVO;
import zack.project.domain.award.service.IAwardService;
import zack.project.domain.credit.model.entity.CreditAccountEntity;
import zack.project.domain.credit.model.entity.TradeEntity;
import zack.project.domain.credit.model.valobj.TradeNameVO;
import zack.project.domain.credit.model.valobj.TradeTypeVO;
import zack.project.domain.credit.service.ICreditAdjustService;
import zack.project.domain.rebate.model.entity.BehaviorEntity;
import zack.project.domain.rebate.model.entity.BehaviorRebateOrderEntity;
import zack.project.domain.rebate.model.valobj.BehaviorTypeVO;
import zack.project.domain.rebate.service.IBehaviorRebateService;
import zack.project.domain.strategy.model.entity.RaffleAwardEntity;
import zack.project.domain.strategy.model.entity.RaffleFactorEntity;
import zack.project.domain.strategy.service.IRaffleStrategy;
import zack.project.domain.strategy.service.armory.IStrategyArmory;
import zack.project.trigger.api.IRaffleActivityService;
import zack.project.trigger.api.dto.*;
import zack.project.trigger.api.response.Response;
import zack.project.types.annotation.DCCValue;
import zack.project.types.annotation.RateLimiterAccessInterceptor;
import zack.project.types.enums.ResponseCode;
import zack.project.types.exception.AppException;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * @author A1793
 */
@Slf4j
@RestController()
@CrossOrigin("${app.config.cross-origin}")
@RequestMapping("/api/${app.config.api-version}/raffle/activity/")
@Service(version = "1.0")

public class RaffleActivityController implements IRaffleActivityService {

    private final SimpleDateFormat dateFormatDay = new SimpleDateFormat("yyyyMMdd");

    @Resource
    private IRaffleActivityPartakeService partakeService;
    @Resource
    private IRaffleStrategy raffleStrategy;
    @Resource
    private IAwardService awardService;
    @Resource
    private IActivityArmory activityArmory;
    @Resource
    private IStrategyArmory strategyArmory;
    @Resource
    private IBehaviorRebateService rebateService;
    @Resource
    private IRaffleActivityAccountQuotaService raffleActivityAccountQuotaService;
    @Resource
    private IRaffleActivitySkuProductService raffleActivitySkuProductService;
    @Resource
    private ICreditAdjustService creditAdjustService;
    // dcc 统一配置中心动态配置降级开关
    @DCCValue("degradeSwitch:close")
    private String degradeSwitch;

    @RequestMapping(value = "armory", method = RequestMethod.GET)
    @Override
    public Response<Boolean> armory(@RequestParam Long activityId) {
        try {
            log.info("活动装配，数据预热，开始 activityId:{}", activityId);

            //根据活动id装配活动余额，
            activityArmory.assembleActivitySkuByActivityId(activityId);
            //根据活动id装配抽奖策略
            strategyArmory.assembleLotteryStrategyByActivityId(activityId);
            Response<Boolean> response = new Response<>();
            response.setCode(ResponseCode.SUCCESS.getCode());
            response.setInfo(ResponseCode.SUCCESS.getInfo());
            log.info("活动装配，数据预热，完成 activityId:{}", activityId);

            return response;

        } catch (Exception e) {
            log.error("活动装配，数据预热，失败 activityId:{}", activityId, e);

            Response<Boolean> response = new Response<>();
            response.setCode(ResponseCode.UN_ERROR.getCode());
            response.setInfo(ResponseCode.UN_ERROR.getInfo());
            return response;
        }

    }

    /**
     * 获取一笔抽奖单()
     * @param request 请求对象
     * @return
     */
    @RateLimiterAccessInterceptor(key = "userId",
            fallbackMethod = "drawRateLimiterError",
            permitsPerSecond = 1.0d,
            blacklistCount = 1)
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "150")
    }, fallbackMethod = "drawHystrixError"
    )
    @RequestMapping(value = "draw", method = RequestMethod.POST)
    @Override
    public Response<ActivityDrawResponseDTO> draw(@RequestBody ActivityDrawRequestDTO request) {

        try   {
            log.info("活动抽奖 userId:{} activityId:{}", request.getUserId(), request.getActivityId());
            if (StringUtils.isNotBlank(degradeSwitch) && "open".equals(degradeSwitch)) {
                return Response.<ActivityDrawResponseDTO>builder()
                        .code(ResponseCode.DEGRADE_SWITCH.getCode())
                        .info(ResponseCode.DEGRADE_SWITCH.getInfo())
                        .build();
            }

            Long activityId = request.getActivityId();
            String userId = request.getUserId();

            //参数校验
            if (activityId == null || userId == null) {
                throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
            }
            //用户消耗参与抽奖活动的次数(数据库表{raffle_activity_account},
            // raffle_activity_account_month},raffle_activity_account_day}),生成一笔抽奖单
            UserRaffleOrderEntity orderEntity = partakeService.
                    createOrder(PartakeRaffleActivityEntity.builder().activityId(activityId).userId(userId).build());

            log.info("活动抽奖，创建订单 userId:{} activityId:{} orderId:{}", request.getUserId(), request.getActivityId(), orderEntity.getOrderId());
            //进行抽奖
            RaffleAwardEntity raffleAwardEntity = raffleStrategy.performRaffle(RaffleFactorEntity.builder()
                    .strategyId(orderEntity.getStrategyId())
                    .userId(userId)
                    .endTime(orderEntity.getEndTime())
                    .build());
            //构建中奖实体，设置活动信息，奖品信息，抽奖单单号
            UserAwardRecordEntity userAwardRecordEntity = new UserAwardRecordEntity();
            userAwardRecordEntity.setUserId(userId);
            userAwardRecordEntity.setActivityId(activityId);
            userAwardRecordEntity.setStrategyId(orderEntity.getStrategyId());
            //用户中奖记录的orderId是用户的抽奖单的orderId
            userAwardRecordEntity.setOrderId(orderEntity.getOrderId());
            userAwardRecordEntity.setAwardId(raffleAwardEntity.getAwardId());
            userAwardRecordEntity.setAwardTitle(raffleAwardEntity.getAwardTitle());
            userAwardRecordEntity.setAwardTime(new Date());
            userAwardRecordEntity.setAwardState(AwardStateVO.create);
            userAwardRecordEntity.setAwardConfig(raffleAwardEntity.getAwardConfig());
            //保存中奖信息,更新抽奖单状态，保存发奖补偿任务，发送发奖消息
            awardService.saveUserAwardRecord(userAwardRecordEntity);

            ActivityDrawResponseDTO activityDrawResponseDTO = new ActivityDrawResponseDTO();
            activityDrawResponseDTO.setAwardId(raffleAwardEntity.getAwardId());
            activityDrawResponseDTO.setAwardTitle(raffleAwardEntity.getAwardTitle());
            activityDrawResponseDTO.setAwardIndex(raffleAwardEntity.getSort());
            return Response.<ActivityDrawResponseDTO>builder().code(ResponseCode.SUCCESS.getCode()).info(ResponseCode.SUCCESS.getInfo()).data(activityDrawResponseDTO).build();
        } catch (AppException e) {
            log.error("活动抽奖失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);
            return Response.<ActivityDrawResponseDTO>builder().code(e.getCode()).info(e.getInfo()).build();
        } catch (Exception e) {
            log.error("活动抽奖失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);
            return Response.<ActivityDrawResponseDTO>builder().code(ResponseCode.UN_ERROR.getCode()).info(ResponseCode.UN_ERROR.getInfo()).build();
        }

    }

    public Response<ActivityDrawResponseDTO> drawRateLimiterError(@RequestBody ActivityDrawRequestDTO request) {
        log.info("活动抽奖限流 userId:{} activityId:{}", request.getUserId(), request.getActivityId());

        return Response.<ActivityDrawResponseDTO>builder().code(ResponseCode.RATE_LIMITER.getCode()).info(ResponseCode.RATE_LIMITER.getInfo()).build();

    }

    public Response<ActivityDrawResponseDTO> drawHystrixError(@RequestBody ActivityDrawRequestDTO request) {
        log.info("活动抽奖熔断 userId:{} activityId:{}", request.getUserId(), request.getActivityId());

        return Response.<ActivityDrawResponseDTO>builder().code(ResponseCode.HYSTRIX.getCode()).info(ResponseCode.HYSTRIX.getInfo()).build();
    }


    @RequestMapping(value = "calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<Boolean> calendarSignRebate(@RequestParam String userId) {
        try {
            log.info("日历签到返利开始 userId:{}", userId);
            //创建行为实体
            BehaviorEntity behaviorEntity = new BehaviorEntity();
            behaviorEntity.setUserId(userId);
            behaviorEntity.setBehaviorTypeVO(BehaviorTypeVO.SIGN);
            //用当前日期作为外部单号
            behaviorEntity.setOutBusinessNo(dateFormatDay.format(new Date()));

            List<String> orderIds = rebateService.createOrder(behaviorEntity);
            log.info("日历签到返利完成 userId:{} orderIds: {}", userId, JSON.toJSONString(orderIds));

            return Response.<Boolean>builder().
                    code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo()).
                    data(true).build();

        } catch (AppException e) {
            log.error("日历签到返利异常 userId:{} ", userId, e);
            return Response.<Boolean>builder().code(e.getCode()).info(e.getInfo()).data(false).build();
        } catch (Exception e) {
            log.error("日历签到返利失败 userId:{}", userId);
            return Response.<Boolean>builder().code(ResponseCode.UN_ERROR.getCode()).info(ResponseCode.UN_ERROR.getInfo()).data(false).build();
        }
    }

    @RequestMapping(value = "is_calendar_sign_rebate", method = RequestMethod.POST)
    @Override
    public Response<Boolean> isCalendarSignRebate(@RequestParam String userId) {

        try {
            log.info("查询用户是否完成日历签到返利开始 userId:{}", userId);

            String outBusinessNo = dateFormatDay.format(new Date());

            List<BehaviorRebateOrderEntity> behaviorRebateOrderEntities =
                    rebateService.queryOrderByOutBusinessNo(userId, outBusinessNo);
            log.info("查询用户是否完成日历签到返利完成 userId:{} orders.size:{}", userId, behaviorRebateOrderEntities.size());

            return Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(!behaviorRebateOrderEntities.isEmpty())
                    .build();
        } catch (Exception e) {
            log.error("查询用户是否完成日历签到返利失败 userId:{}", userId, e);
            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .data(false)
                    .build();
        }
    }

    @RequestMapping(value = "query_user_activity_account", method = RequestMethod.POST)
    @Override
    public Response<UserActivityAccountResponseDTO> queryUserActivityAccount(@RequestBody UserActivityAccountRequestDTO request) {

        try {

            log.info("查询用户活动账户开始 userId:{} activityId:{}", request.getUserId(), request.getActivityId());

            //参数判断
            if (StringUtils.isBlank(request.getUserId()) || request.getActivityId() == null) {

                throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo());
            }
            ActivityAccountEntity activityAccountEntity = raffleActivityAccountQuotaService.queryActivityAccountEntity(request.getUserId(), request.getActivityId());

            UserActivityAccountResponseDTO userActivityAccountResponseDTO = UserActivityAccountResponseDTO.builder()
                    .totalCount(activityAccountEntity.getTotalCount())
                    .totalCountSurplus(activityAccountEntity.getTotalCountSurplus())
                    .dayCount(activityAccountEntity.getDayCount())
                    .dayCountSurplus(activityAccountEntity.getDayCountSurplus())
                    .monthCount(activityAccountEntity.getMonthCount())
                    .monthCountSurplus(activityAccountEntity.getMonthCountSurplus())
                    .build();
            log.info("查询用户活动账户完成 userId:{} activityId:{} dto:{}", request.getUserId(), request.getActivityId(), JSON.toJSONString(userActivityAccountResponseDTO));

            return Response.<UserActivityAccountResponseDTO>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(userActivityAccountResponseDTO)
                    .build();
        } catch (Exception e) {
            log.error("查询用户活动账户失败 userId:{} activityId:{}", request.getUserId(), request.getActivityId(), e);

            return Response.<UserActivityAccountResponseDTO>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }
    }

    @RequestMapping(value = "query_sku_product_list_by_activity_id", method = RequestMethod.POST)
    @Override
    public Response<List<SkuProductResponseDTO>> querySkuProductListByActivityId(Long activityId) {

        try {
            List<SkuProductEntity> skuProductEntities = raffleActivitySkuProductService.querySkuProductEntityListByActivityId(activityId);

            ArrayList<SkuProductResponseDTO> skuProductResponseDTOS = new ArrayList<>(skuProductEntities.size());

            for (SkuProductEntity skuProductEntity : skuProductEntities) {
                SkuProductResponseDTO.ActivityCount activityCount = new SkuProductResponseDTO.ActivityCount();
                activityCount.setDayCount(skuProductEntity.getActivityCount().getDayCount());
                activityCount.setMonthCount(skuProductEntity.getActivityCount().getMonthCount());
                activityCount.setTotalCount(skuProductEntity.getActivityCount().getTotalCount());

                SkuProductResponseDTO skuProductResponseDTO = SkuProductResponseDTO.builder()
                        .sku(skuProductEntity.getSku())
                        .activityId(skuProductEntity.getActivityId())
                        .activityCountId(skuProductEntity.getActivityCountId())
                        .productAmount(skuProductEntity.getPayAmount())
                        .stockCount(skuProductEntity.getStockCount())
                        .stockCountSurplus(skuProductEntity.getStockCountSurplus())
                        .activityCount(activityCount)
                        .build();
                skuProductResponseDTOS.add(skuProductResponseDTO);
            }
            log.info("查询sku商品集合完成 activityId:{} skuProductResponseDTOS:{}", activityId, JSON.toJSONString(skuProductResponseDTOS));

            return Response.<List<SkuProductResponseDTO>>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(skuProductResponseDTOS)
                    .build();

        } catch (Exception e) {
            log.error("查询sku商品集合失败 activityId:{}", activityId, e);

            return Response.<List<SkuProductResponseDTO>>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();
        }


    }

    @RequestMapping(value = "query_user_credit_account", method = RequestMethod.POST)
    @Override
    public Response<BigDecimal> queryUserCreditAccount(String userId) {

        try {
            log.info("查询用户积分值开始 userId:{}", userId);

            CreditAccountEntity creditAccountEntity = creditAdjustService.queryUserCreditAccount(userId);
            log.info("查询用户积分值完成 userId:{} adjustAmount:{}", userId, creditAccountEntity.getAdjustAmount());

            return Response.<BigDecimal>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(creditAccountEntity.getAdjustAmount())
                    .build();
        } catch (Exception e) {
            log.error("查询用户积分值失败 userId:{}", userId, e);
            return Response.<BigDecimal>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .build();

        }
    }

    /**
     * 积分兑换sku商品增加抽奖次数
     * 调额领域先创建一笔活动参与单(根据不同的tradeType获取不同的tradePolicy)，
     * 根据参与单的outTradeNo，sku商品的价格去调整用户积分(扣减积分，并发送积分调整成功消息，异步将参与单状态改为完成并增加用户抽奖次数)
     * @param request 请求对象「用户ID、商品ID」
     * @return
     */
    @RequestMapping(value = "credit_pay_exchange_sku", method = RequestMethod.POST)
    @Override
    public Response<Boolean> creditPayExchangeSku(@RequestBody SkuProductShopCartRequestDTO request) {

        try {


            log.info("积分兑换商品开始 userId:{} sku:{}", request.getUserId(), request.getSku());
            //需要支付的充值单则扣减sku库存并插入一条参与单（raffle_activity_order）
            UnpaidActivityOrderEntity unpaidActivityOrderEntity =
                    raffleActivityAccountQuotaService.createOrder(SkuRechargeEntity.builder()
                    .sku(request.getSku())
                    .userId(request.getUserId())
                    .outBusinessNo(RandomStringUtils.randomNumeric(12))
                    .orderTradeTypeVO(OrderTradeTypeVO.credit_pay_trade)
                    .build());

            log.info("积分兑换商品，创建订单完成 userId:{} sku:{} outBusinessNo:{}", request.getUserId(), request.getSku(), unpaidActivityOrderEntity.getOutBusinessNo());
            //根据对应的sku商品生成的未支付的参与单订单创建交易实体实体，进行交易，如果是积分交易若成功则会发送一个积分调额成功的消息
            String orderId = creditAdjustService.createOrder(TradeEntity.builder()
                    .userId(unpaidActivityOrderEntity.getUserId())
                    //外部订单id，这里是上面生成的未支付的参与单
                    .outBusinessNo(unpaidActivityOrderEntity.getOutBusinessNo())
                    .amount(unpaidActivityOrderEntity.getPayAmount().negate())
                    //对积分的调整有两种，一种是兑换抽奖次数的扣减，一种用户行为返利奖励积分
                    .tradeNameVO(TradeNameVO.CONVERT_SKU)
                    .tradeTypeVO(TradeTypeVO.REVERSE)
                    .build());
            log.info("积分兑换商品，支付订单完成 userId:{} sku:{} orderId:{}", request.getUserId(), request.getSku(), orderId);

            return Response.<Boolean>builder()
                    .code(ResponseCode.SUCCESS.getCode())
                    .info(ResponseCode.SUCCESS.getInfo())
                    .data(true)
                    .build();

        } catch (Exception e) {
            log.error("积分兑换商品失败 userId:{} sku:{}", request.getUserId(), request.getSku(), e);

            return Response.<Boolean>builder()
                    .code(ResponseCode.UN_ERROR.getCode())
                    .info(ResponseCode.UN_ERROR.getInfo())
                    .data(false)
                    .build();
        }
    }


}
